#
# This file is part of uscp, an open-source cross-platform package tool
# 2008  Rafael Sachetto
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Contact e-mail: Rafael Sachetto <rsachetto@gmail.com>
# Program URL   : http://code.google.com/p/uspc/

from PyQt4.QtCore import QThread, SIGNAL, QString
import urllib2
from BeautifulSoup import BeautifulSoup
import socket
from defines import CACHE_PATH
import cPickle as pickle
import os

# timeout in seconds
timeout = 90
socket.setdefaulttimeout(timeout)

class DowloadPageException(Exception):
    def __init__(self, value):
        self.parameter = value
    def __str__(self):
        return repr(self.parameter)

class ListThread(QThread):

    def __init__(self,parent=None):

        QThread.__init__(self,parent)
        self.release = ""
        self.package = ""
        self.arch = ""
        self.listDeps = True
        self.packages = dict()
        self.packageCache = None
        self.useCache = False
        self.downloadDefault = False;
        self.defaultPackages = []
        self.packageQueue = []

    def loadCache(self):
        try:
            cache_file = open(CACHE_PATH, 'rb')
            self.packageCache = pickle.load(cache_file)
            cache_file.close()
        except IOError:
            print "No cache file found in ", CACHE_PATH
            self.packageCache = dict()

    def enqueueFile(self, package, release, arch, listDeps = True, useCache = False, downloadDefault = False):

        self.dist = str(release)
        self.package = str(package)
        self.arch = str(arch)
        self.listDeps = listDeps
        self.downloadDefault = downloadDefault
        self.useCache = useCache

        if self.useCache:
            if self.packageCache == None:
                self.loadCache()

        if not self.downloadDefault:
            packModule = "%s_%s_%s" % (self.dist_name, release, arch)

            try:
                exec('import %s' % packModule)
                exec('self.defaultPackages = %s.packages' % packModule)
            except ImportError:
                #FIXME: If don't to anything, but we should warn the user
                #that we could not import the default package list!
                pass

        if not self.isRunning():
            self.start();

    def run(self):

        if self.listDeps:
            self.nonRecursiveFindAllDeps()
        else:
            self.addToList();

        if self.useCache:
            self.packageCache.update(self.packages)
            self.pickleVar(self.packageCache, CACHE_PATH)

        self.emit(SIGNAL("endFetchingList()"))

        if not self.downloadDefault:
            del self.defaultPackages


    def addToList(self):
        package = self.package
        self.packageQueue.append(package)
        while self.packageQueue:
            package = self.packageQueue.pop(0)
            self.addToGuiList(package)


    def getPackageInformations(self, package):

        dist = self.dist
        arch = self.arch

        if "-udeb" in package:
            print "Debian installer package!!"
            return "",""


        print "Getting informations about %s" % (package)

        if self.useCache:
            try:
                url, size = self.packageCache[(package, dist, arch,)]
                print "Package %s is in the cache. Returning it!" % (package)
                return url, size
            except KeyError:
                print ("Package %s is not in the cache. Fetching from web site!"
                        % (package))


        page_url =  self.DOWNLOAD_URL_TEMPLATE % (dist, arch, package)

        try:
            html = self.downloadPage(page_url)
        except DowloadPageException, e:
            print e
            print "Putting %s in the package queue again" % package
            self.packageQueue.append(package)
            return "", ""

        soup = BeautifulSoup(html)

        try:
            url = str(soup.find('div', 'cardleft').find('a')['href'])
        except AttributeError:
            aux = soup.find('div', 'documentContent')
            url = str(aux.findAll('a')[2]['href'])
        except TypeError:
            url = self.URL_TEMPLATE % (dist, package)
            try:
                html = self.downloadPage(url)
                soup = BeautifulSoup(html)

                title = soup.find('title').contents[-1]

                if "Error" in title:
                    print "The package %s Doesn't exist!!" % (package)
                    self.emit(SIGNAL("error(const QString &)"),
                                            QString(package))
                    return "", ""
                else:
                    #The package is virtual, get the first option and put in the
                    #queue
                    package_html = soup.find("div", id="pdeps").find("a")
                    package_name = package_html.contents[0]

                    #put the package in the queue
                    self.packageQueue.append(package_name)
                    print "Virtual Package!!"
                    return "", ""

            except DowloadPageException, e:
                print e
                return "", ""

        size = int(soup.find('td', 'size').contents[0].split()[0])

        return url, size

    def getPackageDeps(self, package):



        print "Getting deps of %s" % (package)

        dist = self.dist
        arch = self.arch

        url = self.URL_TEMPLATE % (dist, package)

        try:
            html = self.downloadPage(url)
        except DowloadPageException:
            raise

        soup = BeautifulSoup(html)

        try:
            uls = soup.findAll('ul', 'uldep')[1]
        #The package can be virtual or cannot have dependencies
        except IndexError:
            meta = soup.findAll('meta')[-1]["content"]

            if "virtual" in meta:
                #Virtual package, get the first option
                print "Virtual package!!"
                return
            else:
                print "Package %s doesn't have dependencies" % (package)
                return

        #get each package in the list
        for li in uls.findAll('li'):

            try:
                pack = str(li.find('a').contents[0])
            except:
                print "Package not Available"
                break

            if self.downloadDefault:
                if (pack, dist, arch,) not in self.packages:
                    self.packageQueue.append(pack)
                    print "Appending %s to queue" % pack
            else:
                if pack in self.defaultPackages:
                    print "Package %s come by default in Ubuntu" % (pack)
                    self.emit(SIGNAL("updateBar()"))
                else:
                    if (pack, dist, arch,) not in self.packages:
                        self.packageQueue.append(pack)
                        print "Appending %s to queue" % pack


    def addToGuiList(self, pack):

        dist = self.dist
        arch = self.arch
        unit = 1024 #Kb

        pack_url, size = self.getPackageInformations(pack)
        if pack_url != "":
            converted_size = float(size)/unit
            self.packages[(pack, dist, arch,)] = (pack_url, size)
            self.emit(SIGNAL("updateList(const QString &, const QString &, float, const QString &, const QString &)"),
                       QString(pack), QString(pack_url), converted_size, QString(dist), QString(arch))

    def nonRecursiveFindAllDeps(self):

        package = self.package
        dist = self.dist
        arch = self.arch

        #Get the first packages
        self.addToGuiList(package)
        self.getPackageDeps(package)

        while self.packageQueue:
            pack = self.packageQueue.pop(0)
            if (pack, dist, arch,) not in self.packages:
                try:
                    self.addToGuiList(pack)
                    self.getPackageDeps(pack)
                except DowloadPageException, e:
                    print e
                    print "Putting package %s in the queue again" % pack

    def pickleVar(self, var, filename):

        print "Saving file:", filename
        try:
            output = open(filename, 'wb')
            pickle.dump(var, output)
            output.close()
        except IOError:
            path = os.path.dirname(CACHE_PATH)
            if not os.path.exists(path):
                print "Creating dir ", path
                os.mkdir(path)


    def downloadPage(self, page_url):

        try:
            response = urllib2.urlopen(page_url)
            html = response.read()
            response.close()
            return html
        except:
            raise DowloadPageException("Error getting page %s" %
                                        page_url)

